首先看下面这段代码:

1
2
3
4
console.log(0)
setTimeout(() => console.log(1),0)
Promise.resolve(2).then(val => console.log(val))
console.log(3)

按照直观映像, setTimeout的延时为0, console应该立即执行, 应该输出的是 0 1 2 3 然后运行代码后我们发现输出的结果为 0 3 2 1, 这是为什么呢? (因为setTimeout最小执行时间为4ms啊, 笑)

我们知道javascript是单线程的, 但是由于异步的存在, 可以”在同一时间做两件事情”;这是由于js存在两种task,一种为macrotask,比如:整体js代码,setTimeout,setInterval,setImmediate, I/O以及ui render,还有一种为microtask,比如process.nextTick,浏览器的原生Promise,Object.observe,MutationObserver。每次macrotask的代码执行后都会检查microtask内有没有代码,如果有,则取出所有的任务进行执行。

所以当浏览器执行js代码时也就创建了一个macrotask,macrotask可以有多个但是microtask只能有一个,所以以上代码经历的过程为:

  • 浏览器执行一个macrotask(整体js代码)
  • 检查microtask(每次macrotask执行完都会检查,后面过程就不再添加这个动作了,默认有)
  • 执行console.log(0)
  • 创造一个新的macrotask,为console.log(1),挂起
  • 创造了一个microtask,Promise.resolve(2).then(val => console.log(val)),挂起
  • 执行console.log(3),console出了3
  • 检查microtask,有microtask的话将其中所有的任务全部取出进行执行,这里只有一个
  • 执行console.log(2);
  • 整体js代码的这个macrotask任务全部执行完成
  • 执行下一个macrotask,就是setTimeout所在的这个,即执行了console.log(3)
  • 此时没有任何任务队列, 代码执行完毕